import cv2
import numpy as np
from PIL import Image
from paddleocr import PaddleOCR
import sys
import zxingcpp

# image = cv2.imread('./data/images/3.jpg')
# image = cv2.imread('./data/images/2_1.png')
# image = cv2.imread('./data/images/3_1.png')
# image = cv2.imread('./data/images/6_1.png')
# image = cv2.imread('./data/images/8.jpg')
# image = cv2.imread('./data/images/10.jpg')# 不行
# image = cv2.imread('./data/images/11.jpg')
# image = cv2.imread('./data/images/12.jpg') #不行
# image = cv2.imread('./data/images/13.jpg')# 不行
# image = cv2.imread('./data/images/14.jpg')
image = cv2.imread('./data/images/16.jpg')
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray_image", gray_image)
# 根据色差进行二值化，以纯白色为基础
BGR = np.array([255, 255, 255])
upper = BGR + 80
lower = BGR - 80
mask = cv2.inRange(image, lower, upper)
cv2.imshow("mask", mask)
# 获取所有轮廓
contours, _ = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

def find_largest_rectangle(contours):
    max_area = 0
    best_contour = None
    for contour in contours:
        rect = cv2.minAreaRect(contour)
        box = cv2.boxPoints(rect)
        box = np.int64(box)  # 将坐标转换为整数类型
        area = cv2.contourArea(contour)
        if area > max_area:
            max_area = area
            best_contour = contour
            best_rect = rect
    return best_contour, best_rect, max_area

# 获取近似为矩形的最大图形坐标
best_contour, best_rect, max_area = find_largest_rectangle(contours)

# 绘制最大矩形
max_rectangle = image.copy()
box = cv2.boxPoints(best_rect)
box = np.int64(box)  # 将坐标转换为整数类型
cv2.drawContours(max_rectangle, [box], 0, (0, 255, 0), 2)  # 在原图上绘制矩形
cv2.imshow("max_rectangle", max_rectangle)

# 多边形拟合凸包的四个顶点
def getBoxPoint(contour):
    # 多边形拟合凸包
    hull = cv2.convexHull(contour)
    epsilon = 0.02 * cv2.arcLength(contour, True)
    approx = cv2.approxPolyDP(hull, epsilon, True)
    approx = approx.reshape((len(approx), 2))
    return approx

# 适配原四边形点集
def adaPoint(box, pro):
    box_pro = box
    if pro != 1.0:
        box_pro = box/pro
    box_pro = np.trunc(box_pro)
    return box_pro

# 四边形顶点排序，[top-left, top-right, bottom-right, bottom-left]
def orderPoints(pts):
    rect = np.zeros((4, 2), dtype="float32")
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]
    diff = np.diff(pts, axis=1)
    rect[1] = pts[np.argmin(diff)]
    rect[3] = pts[np.argmax(diff)]
    return rect

# 透视变换
def warpImage(image, box):
    w, h = pointDistance(box[0], box[1]), \
        pointDistance(box[1], box[2])
    dst_rect = np.array([[0, 0],
                        [w - 1, 0],
                        [w - 1, h - 1],
                        [0, h - 1]], dtype='float32')
    M = cv2.getPerspectiveTransform(box, dst_rect)
    warped = cv2.warpPerspective(image, M, (w, h))
    return warped

def pointDistance(a, b):
        return int(np.sqrt(np.sum(np.square(a - b))))

# 求四个顶点
boxes = getBoxPoint(box)
ratio = 1
# 适配原四边形点集
boxes = adaPoint(boxes, ratio)
# 四边形顶点排序
boxes = orderPoints(boxes)
# 透视变化
warped = warpImage(image, boxes)
cv2.imshow("warped", warped)
ocr = PaddleOCR(
        use_angle_cls=True,
        lang='ch',
        # det_model_dir='models/ch_PP-OCRv3_det_infer', 
        # rec_model_dir='models/ch_PP-OCRv3_rec_infer'
        # det_model_dir='models/ch_ppocr_mobile_v2.0_det_prune_infer', 
        # rec_model_dir='models/ch_ppocr_mobile_v2.0_rec_slim_infer'
    )
# 执行OCR检测
warped_copy = warped.copy()
result = ocr.ocr(warped_copy, cls=True)

# 提取每个多边形的最左下顶点  
def find_lowest_leftmost_point(points):
    # 方案其实很简单，获得x轴最小的两个坐标，然后判断谁的y更小，谁就是左下角顶点
    if not points:
        return None  # 如果列表为空，返回None
    # 根据x坐标排序
    sorted_coordinates = sorted(points, key=lambda x: x[0])
    # 获取最小的两个x坐标的元组
    min_two_coordinates = sorted_coordinates[:2]
    if(min_two_coordinates[0][1] > min_two_coordinates[1][1]):
        return min_two_coordinates[0]
    else:
        return min_two_coordinates[1]


all_leftmost = []
all_points = []
# 遍历检测结果并绘制矩形框
for line in result:
    # 获取文字区域坐标点(左上、右上、右下、左下)
    for points,txt in line:
        # 如果只有一个字的，则去掉
        if len(txt[0]) < 2:
            continue
        int_points = [[int(element) for element in row] for row in points]
        # 定义多边形顶点（需为 np.array 且 dtype=int32）
        pts = np.array(int_points, np.int32)
        # 将顶点包装成"列表的列表"（因 polylines 支持多多边形）
        pts = pts.reshape((-1, 1, 2))  # 变形为 (N,1,2) 格式
        # 绘制多边形框线‌:ml-citation{ref="5" data="citationList"}
        cv2.polylines(warped_copy, [pts], isClosed=True, 
                        color=(0, 255, 0), thickness=2)
        # 二维数组转二维坐标元组
        points_tuple_list = list(map(tuple, int_points))
        print(points_tuple_list, txt)
        # 收集最左下顶点
        leftmost_point = find_lowest_leftmost_point(points_tuple_list)
        all_leftmost.append(leftmost_point)
        print("左下角顶点=", leftmost_point)
        cv2.circle(warped_copy, leftmost_point, 5, (0, 0, 255), -1)
        # 收集每个图形的所有坐标
        all_points.append(points_tuple_list)

# 排序取最下角坐标（通常是医院名称）
print("all_leftmost", all_leftmost)
sorted_points = sorted(all_leftmost, key=lambda p: (p, p))  
hospital_name_point = sorted_points[:1][0]
print("医院的坐标:", hospital_name_point)
# 设置查找范围
width, height = warped_copy.shape[:2]
print("size=", width, height)
x = int(width/1321 * 220)
y = int(height/1629 * 150)
print(x, y)
range_point = (hospital_name_point[0] + x, hospital_name_point[1] - y)
print("range_point", range_point)
cv2.circle(warped_copy, range_point, 5, (0, 0, 255), -1)
person_name_left_point = []
# 查找符合条件的左下角顶点
print("\n\n")
for current_point in all_leftmost:
    # if current_point[0] <= range_point[0] & current_point[1] >= range_point[1]:
    #     if hospital_name_point[0] != current_point[0] or hospital_name_point[1] != current_point[1]:
    #         person_name_left_point.extend([current_point[0], current_point[1]])
    if current_point[0] >= hospital_name_point[0] and current_point[0] <= range_point[0] and\
            current_point[1] <= hospital_name_point[1] and current_point[1] >= range_point[1]:
                    if hospital_name_point[0] != current_point[0] or hospital_name_point[1] != current_point[1]:
                        person_name_left_point.extend([current_point[0], current_point[1]])
        
print("person_name_left_point=", person_name_left_point)
if len(person_name_left_point) == 0:
    print('未获取到名称位置')
    sys.exit(0)
cv2.circle(warped_copy, person_name_left_point, 5, (0, 255, 255), -1)

person_name_points = []
for points in all_points:
    for point in points:
        if person_name_left_point[0] == point[0] and person_name_left_point[1] == point[1]:
            person_name_points = points
            print("矩形坐标组=", points)


print(person_name_points)
# # 1. 创建空白画布（白色背景，RGB 通道）
# image = np.ones((400, 400, 3), dtype=np.uint8) * 255  # 400x400 白色图像

# # 2. 将坐标转换为 OpenCV 需要的数组格式
# # 格式要求：三维数组，形状为 (1, N, 2)，数据类型为 int32
pts = np.array(person_name_points, dtype=np.int32).reshape((-1, 1, 2))

# # 3. 填充多边形内部（颜色为绿色）
cv2.fillPoly(warped_copy, [pts], color=(0, 255, 0))  # BGR 格式颜色

# # 4. 绘制多边形边框（颜色为红色，线宽为2像素）
cv2.polylines(warped_copy, [pts], isClosed=True, color=(0, 0, 255), thickness=2)

# # 5. 显示结果
# cv2.imshow("Filled Polygon", image)

# # 保存结果
cv2.imshow("dimension text", warped_copy)

codes = zxingcpp.read_barcodes(warped_copy)
for r in codes:
    pt1 = (r.position.top_left.x, r.position.top_left.y)
    pt2 = (r.position.bottom_right.x, r.position.bottom_right.y)
    
    # 绘制矩形
    cv2.rectangle(warped_copy, pt1, pt2, (0, 255, 0), -1)
    min_left_x = r.position.top_left.x if r.position.top_left.x < r.position.bottom_left.x else r.position.bottom_left.x
    max_right_x = r.position.top_right.x if r.position.top_right.x > r.position.bottom_right.x else r.position.bottom_right.x
    cv2.circle(warped_copy, (r.position.top_left.x, r.position.top_left.y), 5, (0, 0, 255), -1)
    cv2.circle(warped_copy, (r.position.top_right.x, r.position.top_right.y), 5, (0, 0, 255), -1)
    cv2.circle(warped_copy, (r.position.bottom_left.x, r.position.bottom_left.y), 5, (0, 0, 255), -1)
    cv2.circle(warped_copy, (r.position.bottom_right.x, r.position.bottom_right.y), 5, (0, 0, 255), -1)
    print(f"Rotation:      {r.orientation}deg")
    print("---")

cv2.imshow("bar", warped_copy)

allLakesImage = image.copy()
cv2.drawContours(
    allLakesImage,         # 原始图像
    contours, # 输入轮廓（推荐使用原始轮廓而非近似后的坐标）
    -1,            # 绘制所有轮廓
    (0, 0, 255),   # BGR颜色值（蓝色）
    2              # 线条粗细
)
# cv2.imshow("all_lakes_image", allLakesImage)
# 按q键退出
if cv2.waitKey(0) & 0xFF == ord('q'):
    cv2.destroyAllWindows()
